//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "cprocess.h"
#include "cmodule.h"
#include "helper.h"
#include <aura.h>

#define LOCK_MODULE_LIST    LOCK_START(m_moduleListLock)
#define UNLOCK_MODULE_LIST  LOCK_END(m_moduleListLock)

#define LOCK_THREAD_LIST    LOCK_START(m_threadListLock)
#define UNLOCK_THREAD_LIST  LOCK_END(m_threadListLock)

CProcess::~CProcess()
{
    if (NULL != m_moduleListLock) {
        AuraCriticalSection_Destroy(m_moduleListLock);
        m_moduleListLock = NULL;
    }
    if (NULL != m_threadListLock) {
        AuraCriticalSection_Destroy(m_threadListLock);
        m_threadListLock = NULL;
    }
}

ECode CProcess::Constructor()
{
    ECode ec = NOERROR;

    m_moduleList.Initialize();
    m_threadList.Initialize();

    ec = AuraCriticalSection_Create(&m_moduleListLock);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    ec = AuraCriticalSection_Create(&m_threadListLock);
    if (FAILED(ec)) goto E_FAIL_EXIT;

    ec = AuraProcess_GetStartInfo((Aura_PWChar)m_name.GetPayload(),
                                (Aura_PWChar)m_args.GetPayload());
    if (FAILED(ec)) goto E_FAIL_EXIT;

    m_processId = AuraProcess_GetCurrentId();
    AuraSystem_GetTime((Aura_SystemTime *)&m_startTime);

    return NOERROR;

E_FAIL_EXIT:
    if (NULL != m_moduleListLock) {
        AuraCriticalSection_Destroy(m_moduleListLock);
        m_moduleListLock = NULL;
    }
    if (NULL != m_threadListLock) {
        AuraCriticalSection_Destroy(m_threadListLock);
        m_threadListLock = NULL;
    }
    return ec;
}

PInterface CProcess::Probe(
    /* [in]  */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(IProcess *)this;
    }
    else if (riid == EIID_IProcess) {
        return (IProcess *)this;
    }
    else if (riid == EIID_IMemoryTrace) {
        return (IMemoryTrace *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CProcess;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_IProcess;
    }

    return NULL;
}

UInt32 CProcess::AddRef()
{
    Int32 nRef = m_Refs.Increment();
    return (UInt32)nRef;
}

UInt32 CProcess::Release()
{
    Int32 nRef;

    nRef = m_Refs.Decrement();
    if (0 == nRef) {
        delete this;
    }
    return (UInt32)nRef;
}

ECode CProcess::Aggregate(
    /* [in] */ AggregateType type,
    /* [in] */ PInterface pObject)
{
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::GetDomain(
    /* [out] */ PInterface *ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::Start(
    /* [in] */ WString wsName,
    /* [in] */ WString wsArgs)
{
    return E_PROCESS_ALREADY_STARTED;
}

ECode CProcess::Kill()
{
    this->Helper_ExitCurrent(3);
    assert(0);
    return NOERROR;
}

ECode CProcess::WaitForExit(
    /* [in]  */ Millisecond32 ms,
    /* [out] */ WaitResult *pResult)
{
    return E_INVALID_OPERATION;
}

ECode CProcess::IsAlive(
    /* [out] */ Boolean *pIsAlive)
{
    if (NULL == pIsAlive) {
        return E_INVALID_ARGUMENT;
    }
    *pIsAlive = TRUE;

    return NOERROR;
}

ECode CProcess::SetPriorityRank(
    /* [in]  */ ThreadPriorityRank priorityRank)
{
    switch (priorityRank) {
        case ThreadPriorityRank_Normal:
        case ThreadPriorityRank_SoftRealTime:
            break;
        case ThreadPriorityRank_HardRealTime:
            return E_NOT_IMPLEMENTED;
        default:
            return E_INVALID_ARGUMENT;
    }

    return AuraProcess_SetPriorityRank(priorityRank);
}

ECode CProcess::GetPriorityRank(
    /* [out] */ ThreadPriorityRank * pPriorityRank)
{
    if (NULL == pPriorityRank) {
        return E_INVALID_ARGUMENT;
    }

    return AuraProcess_GetPriorityRank((Aura_ThreadPriorityRank *)pPriorityRank);
}

ECode CProcess::SetThreadPoolCapacity(
    /* [in]  */ Int32 capacity)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::GetThreadPoolState(
    /* [out] */ Int32 * pCapacity,
    /* [out] */ Int32 * pThreads)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::GetAllThreads(
    /* [out] */ IObjectEnumerator **ppThreads)
{
    IObjectEnumerator *pEnumerator = NULL;

    if (NULL == ppThreads) {
        return E_INVALID_ARGUMENT;
    }

    pEnumerator = new CObjectEnumerator(&m_threadList,
                                   m_threadListLock,
                                   &m_threadListState,
                                   static_cast<IProcess*>(this));
    if (NULL == pEnumerator) {
        return E_OUT_OF_MEMORY;
    }
    *ppThreads = pEnumerator;
    (*ppThreads)->AddRef();

    return NOERROR;
}

ECode CProcess::GetAllModules(
    /* [out] */ IObjectEnumerator **ppModules)
{
    IObjectEnumerator *pEnumerator = NULL;

    if (NULL == ppModules) {
        return E_INVALID_ARGUMENT;
    }

    pEnumerator = new CObjectEnumerator(&m_moduleList,
                               m_moduleListLock,
                               &m_moduleListState,
                               static_cast<IProcess*>(this));
    if (NULL == pEnumerator) {
        return E_OUT_OF_MEMORY;
    }
    *ppModules = pEnumerator;
    (*ppModules)->AddRef();

    return NOERROR;
}

ECode CProcess::GetModuleByName(
    /* [in]  */ WString name,
    /* [out] */ IModule ** ppModule)
{
    CModule * pModule = NULL;

    if (name.IsNullOrEmpty()
       || (NULL == ppModule)) {
        return E_INVALID_ARGUMENT;
    }
    LOCK_MODULE_LIST;
    ForEachDLinkNode(CModule*, pModule, &m_moduleList)
    {
        if (!name.Compare(pModule->m_name, StringCase_Insensitive)) {
            *ppModule = static_cast<IModule *>(pModule);
            (*ppModule)->AddRef();
            UNLOCK_MODULE_LIST;
            return NOERROR;
        }
    }
    UNLOCK_MODULE_LIST;
    return E_DOES_NOT_EXIST;
}

ECode CProcess::GetModuleByAddress(
    /* [in]  */ Address address,
    /* [out] */ IModule * * ppModule)
{
    CModule * pModule = NULL;

    if (NULL == ppModule) {
        return E_INVALID_ARGUMENT;
    }

    LOCK_MODULE_LIST;
    ForEachDLinkNode(CModule*, pModule, &m_moduleList)
    {
#define FALLINTO_AREA(address) \
         ((pModule->m_baseAddr <= address) \
         && ((pModule->m_baseAddr + pModule->m_sizeOfImage) > address))

        if (FALLINTO_AREA(address)) {
            *ppModule = static_cast<IModule*>(pModule);
            (*ppModule)->AddRef();
            UNLOCK_MODULE_LIST;
            return NOERROR;
        }
    }
    UNLOCK_MODULE_LIST;

    return E_DOES_NOT_EXIST;
}

ECode CProcess::GetId(
    /* [out] */ ProcessId * pPid)
{
    if (NULL == pPid) {
        return E_INVALID_ARGUMENT;
    }

    *pPid = m_processId;
    return NOERROR;
}

ECode CProcess::GetStartInfo(
        /* [out] */ WStringBuf * pName,
        /* [out] */ WStringBuf * pArgs)
{
    if ((pName == NULL || !pName->GetCapacity()) &&
        (pArgs == NULL || !pArgs->GetCapacity())) {
        return E_INVALID_ARGUMENT;
    }

    if ((NULL != pName) && (pName->GetCapacity())) {
        pName->Copy(m_name);
    }
    if ((NULL != pArgs) && (pArgs->GetCapacity())) {
        pArgs->Copy(m_args);
    }

    return NOERROR;
}

ECode CProcess::GetExitCode(
    /* [out] */ ExitCode * pExitCode)
{
    if (NULL == pExitCode) {
        return E_INVALID_ARGUMENT;
    }

    *pExitCode = E_PROCESS_STILL_ACTIVE;

    return E_PROCESS_NOT_EXITED;
}

ECode CProcess::GetStartTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }
    *pTime = m_startTime;
    return NOERROR;
}

ECode CProcess::GetExitTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }
    return E_PROCESS_NOT_EXITED;
}

ECode CProcess::GetProcessorTimes(
    /* [out] */ Millisecond32 * pUserTime,
    /* [out] */ Millisecond32 * pPrivilegedTime)
{
    if ((NULL == pUserTime)
     && (NULL == pPrivilegedTime)) {
        return E_INVALID_ARGUMENT;
    }

    return AuraProcess_GetProcessorTimes(
                          (Aura_Millisecond32 *)pUserTime,
                          (Aura_Millisecond32 *)pPrivilegedTime);
}

ECode CProcess::GetMemorySizes(
    /* [out] */ Int32 * pPrivateVirtualMemorySize,
    /* [out] */ Int32 * pSharedVirtualMemorySize,
    /* [out] */ Int32 * pPrivatePhysicalMemorySize,
    /* [out] */ Int32 * pSharedPhysicalMemorySize)
{
    if ((NULL == pPrivateVirtualMemorySize)
        && (NULL == pSharedVirtualMemorySize)
        && (NULL == pPrivatePhysicalMemorySize)
        && (NULL == pSharedPhysicalMemorySize)) {
        return E_INVALID_ARGUMENT;
    }

    return AuraProcess_GetMemoryInformation(
                         (Aura_Int32 *)pPrivateVirtualMemorySize,
                         (Aura_Int32 *)pSharedVirtualMemorySize,
                         (Aura_Int32 *)pPrivatePhysicalMemorySize,
                         (Aura_Int32 *)pSharedPhysicalMemorySize);
}

ECode CProcess::SetTraceArgs(
    /* [in]*/ Int32 wcArgId,
    /* [in]*/ Int32 index,
    /* [in]*/ Address watchAddress)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::SetTraceDebug(
    /* [in] */ Boolean bDebug)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::IsTraceDebug(
    /* [out] */ Boolean *pDebug)
{
    FixMe("Not implemented yet.");
    return E_NOT_IMPLEMENTED;
}

ECode CProcess::Helper_LoadModule(
    /* [in]  */ WString name,
    /* [in]  */ ModuleLoadFlags flags,
    /* [out] */ IModule ** ppModule)
{
    CModule * pModule  = NULL;
    IModule * pIModule = NULL;
    ECode ec = NOERROR;

    assert(!name.IsNullOrEmpty());
    assert(NULL != ppModule);

    LOCK_MODULE_LIST;
    ForEachDLinkNode(CModule*, pModule, &m_moduleList)
    {
        if (!name.Compare(pModule->m_name, StringCase_Insensitive)) {
            *ppModule = static_cast<IModule *>(pModule);
            (*ppModule)->AddRef();
            UNLOCK_MODULE_LIST;
            return NOERROR;
        }
    }
    ec = CModule::New(name, flags, m_moduleListLock, &pIModule);
    if (FAILED(ec)) {
        goto E_FAIL_EXIT;
    }
    UNLOCK_MODULE_LIST;

    *ppModule = pIModule;
    return ec;

E_FAIL_EXIT:
    UNLOCK_MODULE_LIST;
    if (NULL != pIModule) {
        pIModule->Release();
    }
    return ec;
}

void CProcess::Helper_ExitCurrent(
    /* [in] */ ExitCode exitCode)
{
    IThread * pCurrTd = Helper_Thread_GetCurrent();
    IThread * pMainTd = Helper_Thread_GetMain();
    CThread * pThread = NULL;

    LOCK_THREAD_LIST;
    CTHREAD(pCurrTd)->Detach();
    CTHREAD(pCurrTd)->Initialize();
    if (pCurrTd != pMainTd) {
        CTHREAD(pMainTd)->Detach();
        CTHREAD(pMainTd)->Initialize();
    }

    while (!m_threadList.IsEmpty()) {
        pThread = CTHREAD(m_threadList.Next());
        pThread->Detach();
        pThread->Initialize();
        pThread->Abort();
    }
    UNLOCK_THREAD_LIST;

    pCurrTd->Release();
    if (pCurrTd != pMainTd) {
        pMainTd->Release();
    }
    AuraProcess_Exit(exitCode);
    assert(0);
}

void CProcess::Helper_AttachThread(
    /* [in] */ DLinkNode * pThread)
{
    LOCK_THREAD_LIST;
    m_threadList.InsertLast(pThread);
    m_threadListState++;
    UNLOCK_THREAD_LIST;
}

void CProcess::Helper_DetachThread(
    /* [in] */ DLinkNode * pThread)
{
    LOCK_THREAD_LIST;
    pThread->Detach();
    m_threadListState++;
    UNLOCK_THREAD_LIST;
}

void CProcess::Helper_AttachModule(
    /* [in] */ DLinkNode * pModule)
{
    LOCK_MODULE_LIST;
    m_moduleList.InsertLast(pModule);
    m_moduleListState++;
    UNLOCK_MODULE_LIST;
}

void CProcess::Helper_DetachModule(
    /* [in] */ DLinkNode * pModule)
{
    LOCK_MODULE_LIST;
    pModule->Detach();
    m_moduleListState++;
    UNLOCK_MODULE_LIST;
}

ECode CProcess::New(
    /* [out] */ IProcess ** ppProcess)
{
    CProcess * pProcess = NULL;
    ECode ec = NOERROR;

    assert(NULL != ppProcess);

    pProcess = new CProcess();
    if (NULL == pProcess) {
        ec = E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }
    ec = pProcess->Constructor();
    if (FAILED(ec)) {
        goto E_FAIL_EXIT;
    }

    *ppProcess = static_cast<IProcess *>(pProcess);
    (*ppProcess)->AddRef();

    return NOERROR;

E_FAIL_EXIT:
    if (NULL != pProcess) {
        delete pProcess;
    }
    return ec;
}

